package scenario;

import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

import javax.imageio.ImageIO;

import terrain.HighWallRect;
import unit.CaveTroll;
import unit.Goblin;
import fellowship.*;
import equipment.*;
import uniqueEquipment.*;
import main.Army;
import main.Battle;
import main.Board;
import main.Game;
import main.UI;
import main.Unit;
import main.Utils;

public class BalinsTomb extends Game {
	
	ArrayList<Unit> deadGoblins = new ArrayList<Unit>();
	
	ArrayList<Unit> shieldGoblins = new ArrayList<Unit>();
	ArrayList<ArrayList<Unit>> goblinsToEnter = null;
	
	CaveTroll caveTroll = new CaveTroll(20, 1, 0, 0);
			
	public BalinsTomb() {
		Board b = new Board(18, 18);
		File imFile = new File("scenario/rubbleFloor_540_540.png");
		try {
			b.background = ImageIO.read(imFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// TODO: Terrain
		// Tomb
		HighWallRect tomb = new HighWallRect(8.25*Utils.pixelsPerInch, 7.75*Utils.pixelsPerInch, 
				1.5*Utils.pixelsPerInch, 2.5*Utils.pixelsPerInch, 0.01);
		b.addTerrain(tomb);
		
		// Pillars
		HighWallRect p1 = new HighWallRect(3.5*Utils.pixelsPerInch, 3.5*Utils.pixelsPerInch,
											1.25*Utils.pixelsPerInch, 1.25*Utils.pixelsPerInch, 0.01);
		b.addTerrain(p1);
		HighWallRect p2 = new HighWallRect(13.5*Utils.pixelsPerInch, 3.5*Utils.pixelsPerInch,
					1.25*Utils.pixelsPerInch, 1.25*Utils.pixelsPerInch, 0.01);
		b.addTerrain(p2);
		HighWallRect p3 = new HighWallRect(3.5*Utils.pixelsPerInch, 13.5*Utils.pixelsPerInch,
				1.25*Utils.pixelsPerInch, 1.25*Utils.pixelsPerInch, 0.01);
		b.addTerrain(p3);
		HighWallRect p4 = new HighWallRect(13.5*Utils.pixelsPerInch, 13.5*Utils.pixelsPerInch,
				1.25*Utils.pixelsPerInch, 1.25*Utils.pixelsPerInch, 0.01);
		b.addTerrain(p4);
		
		
		// Well
		HighWallRect w1 = new HighWallRect(3.5*Utils.pixelsPerInch, 8.6*Utils.pixelsPerInch,
				0.4*Utils.pixelsPerInch,0.8*Utils.pixelsPerInch,  0.01);
		b.addTerrain(w1);
		HighWallRect w2 = new HighWallRect(3.5*Utils.pixelsPerInch, 8.6*Utils.pixelsPerInch,
				Math.sqrt(2)*0.6*Utils.pixelsPerInch, Math.sqrt(2)*0.2*Utils.pixelsPerInch, Math.PI/4);
		b.addTerrain(w2);
		HighWallRect w3 = new HighWallRect(3.3*Utils.pixelsPerInch, 8.8*Utils.pixelsPerInch,
				0.8*Utils.pixelsPerInch,0.4*Utils.pixelsPerInch,  0.01);
		b.addTerrain(w3);
		HighWallRect w4 = new HighWallRect(3.3*Utils.pixelsPerInch, 9.2*Utils.pixelsPerInch,
				Math.sqrt(2)*0.6*Utils.pixelsPerInch, Math.sqrt(2)*0.2*Utils.pixelsPerInch, 2*Math.PI - Math.PI/4);
		b.addTerrain(w4);
		
		board = b;
		b.game = this;
		
		
		
		// The Fellowhip!
		Unit frodo = new Frodo(1,0,0,0);
		frodo.equip(new Sting());
		frodo.equip(new MithrilCoat());
		Unit samwise = new Samwise(2,0,0,0);
		Unit merry = new Merry(3,0,0,0);
		Unit pippin = new Pippin(4,0,0,0);
		Unit gandalf = new Gandalf(5,0,0,0);
		Unit aragorn = new Aragorn(6,0,0,0);
		aragorn.equip(new Bow());
		Unit boromir = new Boromir(7,0,0,0);
		Unit gimli = new Gimli(8,0,0,0);
		Unit legolas = new Legolas(9,0,0,0);
			
		ArrayList<Unit> good = new ArrayList<Unit>();
		good.add(frodo);
		good.add(samwise);
		good.add(merry);
		good.add(pippin);
		good.add(gandalf);
		good.add(aragorn);
		good.add(boromir);
		good.add(gimli);
		good.add(legolas);
		


		ArrayList<Unit> goblins = new ArrayList<Unit>();
		int numGoblinArchers = 8;
		int numGoblinShield = 8;
		int numGoblinSpear = 8;
		int badID = 21;
		for (int i = 0; i < numGoblinShield; i++) {
			Unit u = new Goblin(badID++,1,0,0);
			u.equip(new Shield());
			shieldGoblins.add(u);
		}
		for (int i = 0; i < numGoblinArchers; i++) {
			Unit u = new Goblin(badID++,1,0,0);
			u.equip(new OrcBow());
			goblins.add(u);
		}
		for (int i = 0; i < numGoblinSpear; i++) {
			Unit u = new Goblin(badID++,1,0,0);
			u.equip(new Spear());
			goblins.add(u);
		}
		Collections.shuffle(goblins);
		
		ArrayList<Unit> goblins1 = new ArrayList<Unit>();
		ArrayList<Unit> goblins2 = new ArrayList<Unit>();
		for (int i = 0; i < 8; i++) {
			goblins1.add(goblins.get(i));
			goblins2.add(goblins.get(i+8));
		}		
		goblinsToEnter = new ArrayList<ArrayList<Unit>>();
		goblinsToEnter.add(goblins1);
		goblinsToEnter.add(goblins2);
		
		
		Army goodArmy = new Army(good, this);
		Army badArmy = new Army(new ArrayList<Unit>(), this);
		goodArmy.addToGame(this);
		badArmy.addToGame(this);
		armies[0] = goodArmy;
		armies[1] = badArmy;
		armiesPlaced = false;
		battles = new ArrayList<Battle>();
		priority = 0;
		currArmy = 0;
		currPhase = "Move";
		
		UI.messageLog.add("Win conditions:");
		UI.messageLog.add("Evil side: Kill Frodo");
		UI.messageLog.add("Good side: Kill the cave troll");
		UI.messageLog.add("Draw: 2 other Fellowship members die before the cave troll");
		
		Shape goodShape = new Ellipse2D.Double(6*Utils.pixelsPerInch, 6*Utils.pixelsPerInch,
												6*Utils.pixelsPerInch, 6*Utils.pixelsPerInch);
		goodStart = new Area(goodShape);
		
		Shape badShape = new Rectangle2D.Double(30*Utils.pixelsPerInch,0,6*Utils.pixelsPerInch, 36*Utils.pixelsPerInch);
		badStart = new Area(badShape);
		badStart.add(new Area(new Rectangle2D.Double(0,30*Utils.pixelsPerInch,30*Utils.pixelsPerInch,6*Utils.pixelsPerInch)));
	}
	
	// place a unit at the closest point to the trapdoor entry
	public void placeAtTrapdoor(Unit u) {
		int centerX = 270;
		int centerY = 135;
		int searchRadius = 3;
		int currX = centerX - searchRadius;
		int currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
		while (!this.canPlace(u, currX, currY)) {
			currX += rng.nextInt(20)+10;
			currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
			if (currX >= centerX+searchRadius) {
				searchRadius += rng.nextInt(20) + 10;
				currX = centerX - searchRadius;
				currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
			}
		}
		u.x = currX;
		u.y = currY;
	}
	
	// place a unit at the nearest point to the door entry
	public void placeAtDoor(Unit u) {
		int centerX = 270;
		int centerY = 700;
		double searchRadius = 180;
		int currX = centerX - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(160, 2));
		int currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
		while (!this.canPlace(u, currX, currY)) {
			currX += rng.nextInt(20)+10;
			currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
			if (currY >= 540) {
				searchRadius += rng.nextInt(20) + 10;
				currX = centerX - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(160, 2));
				currY = centerY - (int)Math.sqrt(Math.pow(searchRadius, 2)-Math.pow(centerX-currX, 2));
			}
		}
		u.x = currX;
		u.y = currY;
	}
	
	
	
	public void endPhase() {
		myUI.message = "";
		// Beginning of first evil move phase, shield goblins move in
		if (currPhase == "Move" && turnNumber == 0 && currArmy == Game.GOODINDEX) {
			while(shieldGoblins.size() != 0) {
				Unit nextGoblin = shieldGoblins.get(0);
				shieldGoblins.remove(0);
				placeAtDoor(nextGoblin);
				armies[Game.BADINDEX].addUnit(nextGoblin);
			}
			armies[Game.BADINDEX].origSize = 1;
		}
		
		// End of third evil move phase, bring in the cave troll
		if (currPhase == "Move" && turnNumber == 2 && currArmy == Game.BADINDEX) {
			placeAtDoor(caveTroll);
			armies[Game.BADINDEX].addUnit(caveTroll);
		}

		// Until goblinsToEnter is empty, roll to bring in the next group
		if (currPhase == "Move" && goblinsToEnter.size() > 0 && currArmy == Game.BADINDEX) {
			int bringInRoll = rng.nextInt(6)+1;
			UI.messageLog.add("Rolling to bring in next group: " + bringInRoll);
			if(bringInRoll >= 4) {
				ArrayList<Unit> bringInGroup = goblinsToEnter.get(0);
				goblinsToEnter.remove(0);
				while(bringInGroup.size() != 0) {
					Unit nextGoblin = bringInGroup.get(0);
					bringInGroup.remove(0);
					// Randomly enter through door or trapdoor
					if (rng.nextBoolean())
						placeAtDoor(nextGoblin);
					else 
						placeAtTrapdoor(nextGoblin);
					armies[Game.BADINDEX].addUnit(nextGoblin);
				}
				armies[Game.BADINDEX].origSize = 1;
			}
		}
		
		// If there are more than 8 dead goblins, bring them in
		if (armies[Game.BADINDEX].removedUnits.size() >= 8 && caveTroll.getWounds() > 0) {
			UI.messageLog.add("More than 8 dead goblins, bringing them back in");
			System.out.println("Removed goblins:");
			for (Unit u : armies[Game.BADINDEX].removedUnits)
				System.out.println(u.toChoiceString());
			ArrayList<Unit> goblinsToRes = armies[Game.BADINDEX].removedUnits;
			for (Unit nextGoblin : goblinsToRes) {
				nextGoblin.setWounds(1);
				armies[Game.BADINDEX].units.add(nextGoblin);
				if (rng.nextBoolean())
					placeAtDoor(nextGoblin);
				else 
					placeAtTrapdoor(nextGoblin);
			}
			armies[Game.BADINDEX].removedUnits = new ArrayList<Unit>();
		}
		
		
		if (currPhase == "Fight") {
			if (battles.size() > 0) {
				myUI.message = "Still battles to fight";
				myUI.repaint();
				return;
			}
			
			int winner = this.winConditions();
			if (winner == 0) {
				UI.messageLog.add("Good side wins!");
				myUI.endGame();
				myUI.repaint();
				return;
			}
			else if (winner == 1) {
				UI.messageLog.add("Bad side wins!");
				myUI.endGame();
				myUI.repaint();
				return;
			}
			else if (winner == 2) {
				UI.messageLog.add("It's a draw!");
				myUI.endGame();
				myUI.repaint();
				return;
			}
			
			// Calculate priority
			int priorityGood = rng.nextInt(6)+1;
			int priorityBad = rng.nextInt(6)+1;
			UI.messageLog.add("Priority rolls: Good-"+priorityGood+" Bad-" + priorityBad);
			if (priorityGood > priorityBad || (priorityGood == priorityBad && priority == 1)){
				priority = 0;
				UI.messageLog.add("Good has priority");
			}		
			else if (priorityBad > priorityGood){
				priority = 1;
				UI.messageLog.add("Bad has priority");
			}
			
			// Refresh moves
			for (Army a : armies) {
				for (Unit u : a.units) {
					u.refresh();
				}
			}
			turnNumber++;
			currArmy = priority;
			
			currPhase = "Move";
		}
		else if (priority == currArmy) {
			currArmy = (currArmy+1)%2;
		}
		else {
			currArmy = priority;
			if (currPhase == "Move") {
				currPhase = "Shoot";
			}
			else if (currPhase == "Shoot") {
				for (Army a : this.armies) {
					for (Unit u : a.units){
						u.moveLeft = 0;
					}
				}
				currPhase = "Fight";
			}
		}
		myUI.repaint();
	}

	
	public int winConditions() {
		// 2 = draw
		// 1 = Bad victory
		// 0 = Good victory
		// -1 = ongoing
		for (Unit u : armies[Game.GOODINDEX].removedUnits){
			if (u instanceof Frodo)
				return 1;
		}
		for (Unit u : armies[Game.BADINDEX].removedUnits){
			if (u instanceof CaveTroll) {
				if (armies[Game.GOODINDEX].removedUnits.size() >= 2)
					return 2;
				else
					return 0;
			}
		}
		return -1;
	}
	
}
